#include <reg52.h>
#define uchar  unsigned char 
#define uint   unsigned int
void Ledshow(uchar a[]);
sbit beep = P1 ^ 5;			//蜂鸣器
sbit SRCLK = P3 ^ 6;		//74HC595引脚
sbit R_CLK = P3 ^ 5;		//
sbit SER = P3 ^ 4;			//
uchar oldchar[8] = { 0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f,0x3f }; //原始密码
uchar newchar[8];												//采集到的8位密码输入
uchar Ledbuff[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 };	//8位数码管输出缓存区
uchar code dzduan[] = { 0x7F,0xBF,0xDF,0xEF,0xF7,0xFB,0xFD,0xFE };		//点阵显示段
uchar error[] = { 0x00,0x63,0x36,0x1C,0x1C,0x36,0x63,0x00 };//错误提示
uchar correct[] = { 0x0c,0x06,0x06,0x0c,0x18,0x30,0x60,0xc0 };//正确提示
uchar warning[] = { 0xff,0xff ,0xff ,0xff ,0xff ,0xff ,0xff ,0xff };//警告提示
uchar clock_flag = 0; //是否开始计时
uchar warning_flag_1 = 0;//是否超时
uchar warning_flag_2 = 0;//发出3秒声光警告
uchar error_flag = 0;//错误计数
uchar sign_flag = 0;//Led点阵显示标志
uchar KeyValue;//矩阵按键值
char Reset_flag = 0;	//重置标志
int Loc = 0;			//当前输入位
int char_Loc = 0;		//当前记录的密码位
int Reset_Loc = 0;		//当前重置的密码位
int finish = 0;			//校验标志位

unsigned char code table[] =
{	0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
	0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71		};
void delay(int i)			//延时函数约10us一次
{	while (i--);		}
void beepvoice(int i)		//蜂鸣器
{	uchar j;
	for (j = 0; j < 100; j++)
	{	beep = ~beep;
		delay(i);	}		}
void KeyDown(void)//按键操作
{	char a = 0;
	int i = 0, j = 0;
	P1 = 0x0f;
	if (P1 != 0x0f)//检测是否按下
	{	delay(1000);//防抖
		if (P1 != 0x0f)//查找对应的按键
		{	P1 = 0X0F;		//列扫描
			switch (P1)
			{	case(0X07):	KeyValue = 0; break;
				case(0X0b):	KeyValue = 1; break;
				case(0X0d): KeyValue = 2; break;
				case(0X0e):	KeyValue = 3; break;		}
			P1 = 0XF0;		//行扫描
			switch (P1)
			{	case(0X70):	KeyValue = KeyValue; break;
				case(0Xb0):	KeyValue = KeyValue + 4; break;
				case(0Xd0): KeyValue = KeyValue + 8; break;
				case(0Xe0):	KeyValue = KeyValue + 12; break;	}	}
		while ((a < 50) && (P1 != 0xf0))	 //判断是否按完
		{		delay(1000000);
			a++;		}
		if (KeyValue >= 0 && KeyValue <= 9)	
		{	ET0 = 1;						//显示数码管
			if (Reset_flag == 0)			//输入密码
			{	sign_flag = 0;				//关闭点阵显示
				for (i = Loc;i>0;i--) { Ledbuff[i] = Ledbuff[i - 1]; }//8位数码管整体向左移位
				Ledbuff[0] = table[KeyValue];
				Loc = Loc + 1;
				if (warning_flag_1 == 0)	//如果是第一次按下则开始计时是否超时
					ET1 = 1;
				if (Loc == 8)				//将输入的8位数存入密码存储区
				{	for (i = 7; i >= 0; i--)
					{	newchar[char_Loc] = Ledbuff[i];
						char_Loc++;	}
					Loc = 0;		}
				if (char_Loc == 8)
				{	char_Loc = 0;		}		}
			else    //重置密码
			{	for (i = Loc; i > 0; i--) { Ledbuff[i] = Ledbuff[i - 1]; }
				Ledbuff[0] = table[KeyValue];
				Loc = Loc + 1;
				if (Loc == 8)
				{	for (i = 7; i >= 0; i--)
					{	oldchar[Reset_Loc] = Ledbuff[i];
						Reset_Loc++;		}
					Reset_Loc = 0;
					Reset_flag = 0;
					ET0 = 0;				//重置完后点阵显示正确
					sign_flag = 1;
					for (i = 0; i <= 7; i++) Ledbuff[i] = 0x00;
					Loc = 0;		}	}	}
		if (KeyValue == 11)			//按下后开始重置密码
		{	Reset_flag = 1;
			error_flag = 0;  //重新记录错误次数
			sign_flag = 0;
			finish = 0;
			Loc = 0;		}
		if (KeyValue == 10)			//按下后进行密码校验
		{	finish = 1;
			ET1 = 0;
			warning_flag_1 = 0;	}	}	}
/*HC595驱动，串行传输数据*/
void HC595SendByte(uchar dat)
{	uchar i;
	SRCLK = 0;
	R_CLK = 0;
	for (i = 0; i < 8; i++)
	{	SER = dat >> 7;
		dat <<= 1;
		SRCLK = 1;
		delay(1);
		SRCLK = 0;	}
	R_CLK = 1;
	delay(1);
	R_CLK = 0;	}
void Ledshow(uchar a[])									//运算符号显示
{	uchar i;
	P0 = ~0x7F;/*消隐*/
	for (i = 0; i < 8; i++)
	{	P0 = dzduan[i];/*列控制*/
		HC595SendByte(a[i]);/*行控制*/
		delay(100);
		HC595SendByte(0x00);	}	}
void Check()
{	int i = 0;
	if (finish)
	{	ET0 = 0;
		for (i = 0; i <= 7; i++) Ledbuff[i] = 0x00;//将数码管显示清空
		i = 0;
		while (i != 8 && oldchar[i] == newchar[i])	//密码比对
			i++;
		if (i == 8)			//正确输出
		{	sign_flag = 1;
			delay(100);	}
		else				//错误输出
		{	sign_flag = 2;
			delay(100);
			error_flag++;		}
		finish = 0;
		for (i = 0; i <= 7; i++) Ledbuff[i] = 0x00; //清空8位数码管
		Loc = 0;
		char_Loc = 0;		}	}
void Ledscan()												//扫描数码管
{	static unsigned char i = 0;
	P0 = 0xff;
	P2 = (P2 & 0xE3) | (i * 4);				//通过138译码器选择点亮的数码管LSA = P2^2 LSB=P2^3 LSC=P2^4
	P0 = Ledbuff[i];
	if (i < 7)
		i++;
	else
		i = 0;	}
void InitTimer()											//定时器0中断初始化
{	TMOD = 0x11;			//定时器初始化 定时器0、1都为方式1（16位定时器）
	TH0 = 0xFC;
	TL0 = 0x17;
	TH1 = (65536 - 50000) % 256;		//一次定时50ms
	TL1 = (65536 - 50000) / 256;
	TR0 = 1;			//启用定时器
	TR1 = 1;
	EA = 1;				//开启总中断
	ET0 = 1;		}
int main()
{	sign_flag = 3;
	InitTimer();		//定时器中断初始化
	while (1)
	{	if (error_flag < 3)		//错误次数小于3键盘可用
		{	if (warning_flag_2 == 0)	//未超时时键盘可用
			{	KeyDown();
				Check();		}
			if (warning_flag_2 > 0)		//超时后键盘锁定，发出警告
				sign_flag = 3;	}
		else					//错误次数等于3锁定键盘，蜂鸣器响，发出警告
		{	beep = ~beep;
			sign_flag = 3;	}
		switch (sign_flag)		//Led点阵显示图样
		{	case 1:Ledshow(correct); break;
			case 2:Ledshow(error); break;
			case 3:Ledshow(warning); break;	}	}	}
void interruptTimer0() interrupt 1	//定时器0用来对8位数码管定时扫描
{	TH0 = 0xFC;
	TL0 = 0x17;
	Ledscan();	}

void interruptTimer1() interrupt 3	//定时器1用来计数，一次z中断表示50ms
{	int i;
	TH1 = (65536 - 50000) / 256;
	TL1 = (65536 - 50000) % 256;
	if (warning_flag_1 < 100)		//小于5s继续计数
	{	warning_flag_1++;	}
	else		//大于5秒
	{	ET0 = 0;
		for (i = 0; i <= 7; i++) Ledbuff[i] = 0x00;		//数码管清空复位
		if (warning_flag_2 < 60)		//蜂鸣器响3s，发出警告  60*50ms=3000ms=3s
		{	beep = ~beep;
			warning_flag_2++; 	}
		else							//蜂鸣器响完后重置锁状态
		{	warning_flag_1 = 0;
			warning_flag_2 = 0;
			ET1 = 0;					
			Loc = 0;
			char_Loc = 0;
			error_flag++;			//错误次数+1
			sign_flag = 0;	}	}	}